package com.example.f1.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.example.f1.domain.Product;
import com.example.f1.domain_param.CartParam;
import com.example.f1.domainmid.CartProduct;
import com.example.f1.service.ProductService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.dayu521.OrRes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/cart")
public class Cart {

    public static final String cart_prefix = "cart:";
    public static final String cart_tik = "cart:tik";
    public static final String p_info_prefix = "p_info:";
    public static final String p_num_prefix = "p_num:";

//    HashMapper<Object, String, Object> mapper = new Jackson2HashMapper(RedisC.objectMapper,false);

    @Resource
    private RedisTemplate<String, ?> redisTemplate;

    @Autowired
    private ProductService productService;


    @PostMapping("/create")
    public OrRes<?> create() {
        //TODO 购物车创建
//        var login_id = StpUtil.getLoginIdAsLong();
//        var hash = redisTemplate.boundHashOps(cart_prefix + login_id);
        return OrRes.ok();
    }

    @PostMapping("/put")
    public OrRes<?> put(@RequestBody CartParam cartParam) {
        var p_id = cartParam.product_id();
        var user_id = cartParam.login_id();
        var hash = redisTemplate.boundHashOps(cart_prefix + user_id);

        var p = hash.get(p_info_prefix + p_id);
        if (p == null) {
            Product product = productService.getById(p_id);
            if (product == null) {
                log.info("没有此商品  {}", p_id);
                return OrRes.error("商品不存在");
            }
            var cartProduct = new CartProduct();
            cartProduct.setId(product.getId().toString());
            cartProduct.setPrice(product.getPrice());
            cartProduct.setTitle(product.getTitle());
            cartProduct.setCreate_time(new Date());

            hash.put(p_info_prefix + p_id, JSONObject.toJSONString(cartProduct));

        }
        // FIXME: 12/13/23 注意,一定要注意redisTemplate的value序列化配置为RedisSerializer.string(),
        //  否则lua执行出问题
        var ok = redisTemplate.execute(RedisScript.of("""
                if redis.call('HEXISTS',KEYS[1],ARGV[1]) > 0
                then
                    redis.call('HINCRBY',KEYS[1],ARGV[2],ARGV[3]);
                    return 0;
                else
                    return -3
                end
                """ /*"return redis.call('HEXISTS',KEYS[1],ARGV[1])"*/ /*"local x=KEYS[1]; return {x}"*/,
                Long.class), List.of(cart_prefix + user_id), p_info_prefix + p_id,p_num_prefix + p_id,cartParam.product_num().orElse(1l).toString());
        if(ok==-3){
            return OrRes.error("购物车增加商品数量失败,没有此商品");
        }
//        hash.increment(p_num_prefix + p_id, cartParam.product_num().orElse(1l));
        return OrRes.ok();
    }

    //这里的不一致性,在结算时总会要同步到一致性为.
    // 而且,这些不一致,用户在结算时发现后,都能通过对购物车的重复的不同操作,解决掉
    @PostMapping("/user/put")
    public OrRes<?> put(@RequestBody String p_id) {
        return put(new CartParam(StpUtil.getLoginIdAsString(), p_id, Optional.of(1l)));
    }

    @PostMapping("/remove")
    public OrRes<?> remove(@RequestBody CartParam cartParam) {
        var login_id = cartParam.login_id();
        var p_id = cartParam.product_id();
        var hash = redisTemplate.boundHashOps(cart_prefix + login_id);

        var res = hash.delete(p_info_prefix + p_id, p_num_prefix + p_id);
        if (res < 2) {
            log.info("购物车商品数据不一致 {}, 每个商品应该存在两个key,实际上 {}", p_id, res);
        }
        return OrRes.ok();
    }

    @PostMapping("/user/remove")
    public OrRes<?> remove(@RequestBody String p_id) {
        return remove(new CartParam(StpUtil.getLoginIdAsString(), p_id, Optional.empty()));
    }

    @PostMapping("/list")
    public OrRes<List<CartProduct>> list(@RequestBody String login_id) {
        var hash = redisTemplate.<String, String>boundHashOps(cart_prefix + login_id);

        var entries = hash.entries();
        var p_ids = new ArrayList<String>(entries.size() / 2);

        entries.keySet().forEach(i -> {
            if (i.startsWith(p_info_prefix)) {
                p_ids.add(i);
            }
        });

        var res = p_ids.stream().map(i -> {

            var p = JSONObject.parseObject(entries.get(i), CartProduct.class);

            p.setNum(Long.parseLong(entries.get(p_num_prefix + i.substring(p_info_prefix.length()))));
            return p;
        }).sorted((a, b) -> {
            return a.getCreate_time().compareTo(b.getCreate_time());
        }).collect(Collectors.toList());

        return OrRes.ok(res);
    }

    @PostMapping("/user/list")
    public OrRes<?> list() {
        return list(StpUtil.getLoginIdAsString());
    }

    @PostMapping("/pid_list")
    public OrRes<?> pid_list(@RequestBody String login_id) {
        var hash = redisTemplate.<String, String>boundHashOps(cart_prefix + login_id);

        var pid = hash.keys().stream().filter(i -> {
            return i.startsWith(p_info_prefix);
        }).map(i -> {
            return Long.parseLong(i.substring(p_info_prefix.length()));
        }).collect(Collectors.toList());

        return OrRes.ok(pid);
    }

    @PostMapping("/user/pid_list")
    public OrRes<?> pid_list() {
        return pid_list(StpUtil.getLoginIdAsString());
    }

    //结算购物车,且返回购物车商品
    @PostMapping("/rob")
    public OrRes<?> rob(@RequestBody CartParam.Rob rob) {
        var login_id = rob.login_id();

        String newKey = cart_prefix + login_id + ":" + rob.settle_id();
        var ok=false;
        try {
            ok=redisTemplate.renameIfAbsent(cart_prefix + login_id, newKey);
        } catch (Exception e) {
            log.error("购物车不存在");
            throw e;
        }
        if(!ok) {
            throw new RuntimeException("已存在newkey="+newKey);
        }
        return list(newKey.substring(cart_prefix.length()));
    }

    @PostMapping("/reverse_rob")
    public OrRes<?> reverse_rob(@RequestBody CartParam.RobReverse robReverse) {
        var login_id = robReverse.login_id();

        String settle_key = cart_prefix + login_id + ":" + robReverse.settle_id();
        var ok = redisTemplate.renameIfAbsent(settle_key, cart_prefix + login_id);
        if (!ok) {//存在购物车,需要重新插入
            log.info("并入购物车");
            var list = list(settle_key.substring(cart_prefix.length()));
            list.res().forEach(i -> {
                put(new CartParam(login_id, i.getId(), Optional.of(i.getNum())));
            });
            redisTemplate.delete(settle_key);
        }

        return OrRes.ok();
    }

    @PostMapping("/cart_state")
    public OrRes<?> cart_state(@RequestBody CartParam cartParam){
        var hash = redisTemplate.<String, String>boundHashOps(cart_prefix + cartParam.login_id());
        Long size = hash.size();
        if(size>0){
            return OrRes.ok(0);
        }else{
            return OrRes.error("购物车为empty");
        }
    }
}
